package com.szsh.aiot.framework.spring.boot;

import com.szsh.aiot.framework.constant.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义日志文件初始化<br>
 * 读取{@link com.szsh.aiot.framework.constant.Constants#CONFIG_FILE_CONTENT_PROPERTY} 配置项<br>
 * 写入{@link LoggingApplicationListener#CONFIG_PROPERTY} 配置项定义的文件中<br>
 * {@link LoggingApplicationListener#CONFIG_PROPERTY}配置说明<br>
 * 以{@link ResourceUtils#CLASSPATH_URL_PREFIX}开头供本地开发使用.例:classpath:logging-config.xml<br>
 * 正式上线需要以{@link ResourceUtils#FILE_URL_PREFIX}开头.例:file:logging-config.xml
 */
public class LogApplicationContextInitializer implements EnvironmentPostProcessor, Ordered {
    public static final int DEFAULT_ORDER = 0;

    private static final Logger LOGGER = LoggerFactory.getLogger(LogApplicationContextInitializer.class);

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        // 日志文件内容
        String text = "${" + Constants.CONFIG_FILE_CONTENT_PROPERTY + "}";
        String logContent = environment.resolvePlaceholders(text);

        if (!StringUtils.hasLength(logContent) || logContent.equals(text)) {
            // 没有配置
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("配置项[{}]未配置", Constants.CONFIG_FILE_CONTENT_PROPERTY);
            }
            return;
        }

        // 自定义处理日志文件
        // 获取日志配置路径
        String logConfig = StringUtils.trimWhitespace(environment.getProperty(LoggingApplicationListener.CONFIG_PROPERTY, ""));
        if (ignoreLogConfig(logConfig)) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("配置[{}]值为[{}],跳过自定义日志初始化", LoggingApplicationListener.CONFIG_PROPERTY, logConfig);
            }
            return;
        }
        // 有配置文件路径
        // 日志文件目录
        String basePath = "";
        // 日志文件名称
        logConfig = SystemPropertyUtils.resolvePlaceholders(logConfig);
        if (logConfig.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
            // 以 classpath: 开头
            logConfig = logConfig.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
            try {
                // 获取 classpath 路径
                basePath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        } else if (logConfig.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
            // 以 file: 开头
            logConfig = logConfig.substring(ResourceUtils.FILE_URL_PREFIX.length());
            // 获取 jar 路径
            basePath = new ApplicationHome().getDir().getAbsolutePath() + File.separator;
        }

        String logConfigFileName = basePath + logConfig;
        try {
            FileCopyUtils.copy(logContent, new FileWriter(logConfigFileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 监控配置变化
        logConfigChange(logConfigFileName);
    }

    @Override
    public int getOrder() {
        try {
            return com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer.DEFAULT_ORDER + 1;
        } catch (Throwable e) {
            // ignored
        }
        return DEFAULT_ORDER;
    }

    private boolean ignoreLogConfig(String logConfig) {
        return !StringUtils.hasLength(logConfig) || logConfig.startsWith("-D");
    }

    private void logConfigChange(String logConfigFileName) {
        try {
            Set<String> interestedKeys = new HashSet<>();
            // 只关心日志配置更新
            interestedKeys.add(Constants.CONFIG_FILE_CONTENT_PROPERTY);
            com.ctrip.framework.apollo.ConfigService.getAppConfig()
                    .addChangeListener(changeEvent -> {
                        if (LOGGER.isInfoEnabled()) {
                            LOGGER.info("[日志文件变化][开始]");
                        }
                        // 获取配置变更
                        com.ctrip.framework.apollo.model.ConfigChange configChange =
                                changeEvent.getChange(Constants.CONFIG_FILE_CONTENT_PROPERTY);
                        // 获取新值
                        String logContent = configChange.getNewValue();
                        if (!(null == logContent || "".equals(logContent.trim()))) {
                            // 不为空
                            try {
                                FileCopyUtils.copy(logContent, new FileWriter(logConfigFileName));
                            } catch (IOException e) {
                                if (LOGGER.isWarnEnabled()) {
                                    LOGGER.warn("[日志文件变化失败]", e);
                                }
                            }
                        }
                        if (LOGGER.isInfoEnabled()) {
                            LOGGER.info("[日志文件变化][结束]");
                        }
                    }, interestedKeys);
        } catch (Throwable e) {
            // ignored
        }
    }
}
